--[[ 四国战机1(日版)脚本,1p2p投币设定为独立
  用世界版改过来的,使用了最新的自定义库MySTG
  
  添加最大火力剩余
  
  By setycyas @2025-02-11
]]

--防止重运行,需用用_G,用重复require不行,因为F4重启可能会把脚本加载到全新模块与原来的不共通
if (_G.game) then
  print('script already loaded!')
  return
end

local myMame = require('MyMame')
local myStg = require('MySTG')

-- 这里设定基本参数
local GAME = {
  -- 脚本名,显示识别运行等使用
  NAME = "sonic_autoboot",
  -- 分辨率
  X_RESOLUTION = 320.0,
  Y_RESOLUTION = 224.0,
  -- Y坐标只需要低位,X坐标需要高低位,无法用读取双字节的方法直接读数据
  YLEFT_ADDR_LOW = 0x000c0711,
  YRIGHT_ADDR_LOW = 0x000c06cf,
  XDOWN_ADDR_LOW = 0x000c0713,
  --XDOWN_ADDR_HIGH = 0x000c0713-1,
  XUP_ADDR_LOW = 0x000c06d1,
  XUP_ADDR_HIGH = 0x000c06d1-1,
  -- 火力地址
  POW_ADDR = 0x000c60e1,
  POW_REMAIN_ADDR_LOW = 0x000c60e7,
  POW_REMAIN_ADDR_HIGH = 0x000c60e7-1,
  -- 中心方块的边框颜色和中心颜色
  BOX_BORDER_COLOR = 0x8800ff00,
  BOX_COLOR = 0x5500ff00,
  -- 边缘参考线的宽度,颜色
  SIDE_LINE_WIDTH = 1,
  SIDE_LINE_COLOR = 0xDDffd700,
  SIDE_LINE_FULL_COLOR = 0xFFff0000, -- 满能量时的颜色
  -- 中途需要变更的测试参数,准备了10个
  TEST_PARAMS = {5, 0x000c60e1, 0, 30, 15, 0.0, 0.0, 0.0, 0.0, 0.0},
  -- mame api用的对象,在refreshDevices()函数刷新获取
  mem = nil,
  container = nil,
  ui_container = nil,
  -- 当前是1p还是2p
  player = 1,
  -- 特定帧任务,只记录按下,按键时间至少相隔5帧以保证上一个键有时间放开
  -- 初始选关任务指令,port接口太多只好记录进去了...
  frame_missions_1p = { --1p以瑞典机开始,+33前3关美国,日本,英国
    [300] = {fieldName = "Coin 1", port = ":IN0", value = 1},
    [340] = {fieldName = "1 Player Start", port = ":IN0", value = 1},
    [380] = {fieldName = "P1 Right", port = ":IN0", value = 1},
    [420] = {fieldName = "P1 Right", port = ":IN0", value = 1},
    [460+33] = {fieldName = "P1 Button 2", port = ":IN0", value = 1}
  },
  frame_missions_2p = {  --2p以美国机开始,+15前3关英国,瑞典,日本;
    [300] = {fieldName = "Coin 2", port = ":IN0", value = 1},
    [340] = {fieldName = "2 Players Start", port = ":IN0", value = 1},
    [380+15] = {fieldName = "P2 Button 2", port = ":IN1", value = 1}
    --[380+5] = {mask = 16, port = ":P2", value = 1}
  }
}
--GAME.frame_missions_1p[460+GAME.TEST_PARAMS[4]]={fieldName="P1 Button 2", port=":IN0", value=1}
--GAME.frame_missions_2p[380+GAME.TEST_PARAMS[5]]={fieldName="P2 Button 2", port=":IN1", value=1}
-- 更新模块设置
myMame.gameName = GAME.NAME
myStg.X_RESOLUTION = GAME.X_RESOLUTION
myStg.Y_RESOLUTION = GAME.Y_RESOLUTION

-- 完成任务指令,每完成一个按键指令添加一个3帧后的放开指令
function GAME.doMissions()
  local portName, field, frame_number, mission, frame_missions
  frame_number = manager.machine.screens[":screen"]:frame_number()
  -- 最大帧数保护,超过直接返回提高运行效率
  if frame_number > 1000 then
    return
  end
  -- 根据player设定选择任务
  if GAME.player == 2 then
    frame_missions = GAME.frame_missions_2p
  else
    frame_missions = GAME.frame_missions_1p
  end
  -- 执行任务
  mission = frame_missions[frame_number]
  if mission then
    portName = mission.port
    -- 对于按下键的情况,添加放开键的操作
    if mission.value > 0 then
      if mission.fieldName then
        frame_missions[frame_number+3] = {fieldName = mission.fieldName, port = portName, value = 0}
      else
        frame_missions[frame_number+3] = {mask = mission.mask, port = portName, value = 0}
      end
    end
    -- 执行操作
    if mission.fieldName then
      field = manager.machine.ioport.ports[portName].fields[mission.fieldName]
    else
      field = manager.machine.ioport.ports[portName]:field(mission.mask)
    end
    field:set_value(mission.value)
    print("mission "..frame_number.." finished!")
  end
end

-- 刷新设备,成功获取设备返回true,否返回false
function GAME.refreshDevices()
  GAME.mem = manager.machine.devices[':maincpu'].spaces['program']
  GAME.container = manager.machine.screens[":screen"].container
  GAME.ui_container = manager.machine.render.ui_container
  
  if (GAME.mem and GAME.container and GAME.ui_container) then
    return true
  else
    return false
  end
end

-- 获取边缘参考线坐标
function GAME.getY()
  local yleft, yright
  yleft = GAME.mem:read_u8(GAME.YLEFT_ADDR_LOW)
  -- 边线可能发生的bug
  if yleft > 253 then
    yleft = 1
  end
  yright = GAME.mem:read_u8(GAME.YRIGHT_ADDR_LOW)
  yright = (yright-1)+(yright-yleft) -- 对称法估计右边界
  return yleft, yright
end
function GAME.getX()
  local xdown, xup
  --xdown = 1.0*(GAME.mem:read_u8(GAME.XDOWN_ADDR_LOW)+GAME.mem:read_u8(GAME.XUP_ADDR_HIGH)*256)
  xup = 1.0*(GAME.mem:read_u8(GAME.XUP_ADDR_LOW)+GAME.mem:read_u8(GAME.XUP_ADDR_HIGH)*256)
  xdown = xup-10
  xup = xup+5 -- 预计略高一点
  return xdown, xup
end

-- 显示监视的数据
function GAME.check()
  local power, powerRemain, str
  power = GAME.mem:read_u8(GAME.POW_ADDR)
  powerRemain = GAME.mem:read_u8(GAME.POW_REMAIN_ADDR_HIGH)*0x100
  powerRemain = powerRemain+GAME.mem:read_u8(GAME.POW_REMAIN_ADDR_LOW)
  
  str = string.format("P=%d...%03x", power, powerRemain)
  GAME.container:draw_text(0, 0, str, 0xFF005500, 0xFFcccccc)
end

-- 监视各地址,测试时用投入使用时不显示
function GAME.watchAddrs()
  local addrArr, strArr, i
  --[[
  addrArr = {
    GAME.YLEFT_ADDR_LOW, GAME.YRIGHT_ADDR_LOW,
    GAME.XDOWN_ADDR_LOW, GAME.XUP_ADDR_LOW, GAME.XUP_ADDR_HIGH,
    GAME.POW_ADDR
  }
  ]]
  addrArr = {}
  for i = 1, 0x10 do
    addrArr[i] = GAME.TEST_PARAMS[2]-0x21+i
  end
  strArr = myMame.readAddrArr(addrArr)
  myMame.showInfo(strArr)
end

-- 显示边缘参考线,坐标为整数
function GAME.sideLines(xdown, xup, yleft, yright)
  local color
  if (GAME.mem:read_u8(GAME.POW_ADDR) < 3) then
    color = GAME.SIDE_LINE_COLOR
  else
    color = GAME.SIDE_LINE_FULL_COLOR
  end
  -- 边缘参考线
  myStg.xLine(xdown, color, GAME.SIDE_LINE_WIDTH)
  myStg.xLine(xup, color, GAME.SIDE_LINE_WIDTH)
  myStg.yLine(yleft, color, GAME.SIDE_LINE_WIDTH)
  myStg.yLine(yright, color, GAME.SIDE_LINE_WIDTH)
  -- 中心框
  myStg.centerBox(
    (xdown+xup)/2.0, (yleft+yright)/2.0, 
    xup-xdown, yright-yleft, 
    GAME.BOX_COLOR
  )
  
end

-- 显示辅助线
function GAME.showAssist()
  GAME.ui_container:draw_box(0.49, 1.0, 0.51, 0.97, 0xDDdd00dd, 0xDDdd00dd) -- 中心
  GAME.ui_container:draw_line(0.0, 0.667, 1.0, 0.667, 0x88dddddd) -- 1/3道具回折线
end

-- 转换1P,2P,参数为2表示2p其他都是1p
function GAME.setPlayer(i)
  
  -- 1p数据
  GAME.player = 1
  GAME.YLEFT_ADDR_LOW = 0x000c0711
  GAME.YRIGHT_ADDR_LOW = 0x000c06cf
  GAME.XDOWN_ADDR_LOW = 0x000c0713
  GAME.XUP_ADDR_LOW = 0x000c06d1
  GAME.XUP_ADDR_HIGH = 0x000c06d1-1
  GAME.POW_ADDR = 0x000c60e1
  GAME.POW_REMAIN_ADDR_LOW = 0x000c60e7
  GAME.POW_REMAIN_ADDR_HIGH = 0x000c60e7-1
  -- 2p数据
  if (i == 2) then
    GAME.player = 2
    GAME.YLEFT_ADDR_LOW = GAME.YLEFT_ADDR_LOW+0x60
    GAME.YRIGHT_ADDR_LOW = GAME.YRIGHT_ADDR_LOW+0x60
    GAME.XDOWN_ADDR_LOW = GAME.XDOWN_ADDR_LOW+0x60
    GAME.XUP_ADDR_LOW = GAME.XUP_ADDR_LOW+0x60
    GAME.XUP_ADDR_HIGH = GAME.XUP_ADDR_HIGH+0x60
    GAME.POW_ADDR = GAME.POW_ADDR+0x100
    GAME.POW_REMAIN_ADDR_LOW = GAME.POW_REMAIN_ADDR_LOW+0x100
    GAME.POW_REMAIN_ADDR_HIGH = GAME.POW_REMAIN_ADDR_HIGH+0x100
  end
  
end

-- 切换1p2p
function GAME.togglePlayer()
  if GAME.player == 2 then
    GAME.setPlayer(1)
  else
    GAME.setPlayer(2)
  end
  print("Player toggled!")
end

-- 主函数,绑定后每一帧执行一次该函数
function GAME.Main()
  local xleft, xright, ydown, yup  
  -- 检查是否能执行,必要!否则F4硬件重启时会出错!
  if (not GAME.refreshDevices()) then
    return
  end
  
  xdown, xup = GAME.getX()
  yleft, yright = GAME.getY()
  
  -- 监视地址
  --GAME.watchAddrs()
  -- 显示数据监视
  GAME.check()
  -- 显示辅助线
  GAME.showAssist()
  -- 完成任务
  --GAME.doMissions()
  -- 坐标合理才显示边缘参考线
  if ((xdown > 0) and (yleft > 0)) then
    GAME.sideLines(xdown, xup, yleft, yright)
  end
  
end

-- [[运行]]

-- 刷新api接口
GAME.refreshDevices()
-- 选1p或2p
GAME.setPlayer(1)
-- 导出到全局变量
_G.game = GAME
-- 绑定每一帧执行的函数,测试时绑定到debug
--myMame.debugRunPerFrame(GAME.Main, 'sonicwi')
myMame.runPerFrame(GAME.Main, 'sonicwi')
-- 绑定热键函数
myMame.setHotkey("KEYCODE_X", GAME.togglePlayer)

return GAME
